﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
using Jayden.Dll.Core.Translations;
using Jayden.Dll.Web.Enums;
using Jayden.Dll.Web.Helper;
using Jayden.Dll.Web.HtmlControls.Descriptions;
using Jayden.Dll.Web.HtmlControls.Interfaces;
using Jayden.Dll.Web.Styles;

namespace Jayden.Dll.Web.HtmlControls.Generics
{
	public abstract class HtmlElement : HtmlControl, WithGlobalAttributes
	{
		protected Dictionary<string, string> m_Attributes;
		protected List<HtmlControl> m_ChildControls;
		private bool m_ServerSafeId = false;

		public HtmlElement(HtmlElementDescription description) : base(description) { }

		new public HtmlElementDescription Description { get { return base.Description as HtmlElementDescription; } }
		public string TagName { get { return Description.TagName; } }
		public bool AllowChildControls { get { return Description.AllowChildControls; } }
		public IEnumerable<string> Attributes
		{
			get
			{
				if (m_Attributes == null)
					return new List<string>();
				return m_Attributes.Keys;
			}
		}

		public void Clear(bool clearAttributes = false)
		{
			if(clearAttributes)
				m_Attributes = null;
			m_ChildControls = null;
		}
		public bool IsVoid { get { return !AllowChildControls && !Description.ForceClosingTag; } }

		private string CheckAttributeName(string attributeName)
		{
			if (string.IsNullOrWhiteSpace(attributeName))
				throw new Exception("Invalid attribute name : '" + attributeName + "'.");
			attributeName = attributeName.ToLowerInvariant().Trim();
			if (Regex.IsMatch(attributeName, "\\s"))
				throw new Exception("Invalid attribute name : '" + attributeName + "'.");
			if(attributeName.Length == 0 || !char.IsLetter(attributeName[0]))
				throw new Exception("Invalid attribute name : '" + attributeName + "'.");
			return attributeName.ToLowerInvariant();
		}

		public uint? GetAttributeUint(string attributeName, bool inherits = false, uint? defaultValue = null)
		{
			string value = GetAttribute(attributeName, true, inherits, defaultValue.HasValue ? defaultValue.Value.ToString() : null);
			if (value == null)
				return null;
			return uint.Parse(value);
		}
		public bool GetAttributeBool(string attributeName, bool inherits = false, bool defaultValue = false)
		{
			string value = GetAttribute(attributeName, true, inherits, defaultValue ? attributeName : null);
			return value == attributeName || value == string.Empty;
		}
		public string GetDataAttribute(string attributeName, string defaultValue) { return GetDataAttribute(attributeName, true, false, defaultValue); }
		public string GetDataAttribute(string attributeName, bool htmlDecode = true, bool inherits = false, string defaultValue = null) { return GetAttribute("data-" + attributeName, htmlDecode, inherits, defaultValue); }
		public string GetServerAttribute(string attributeName, string defaultValue) { return GetDataAttribute("server-" + attributeName, defaultValue); }
		public string GetServerAttribute(string attributeName, bool htmlDecode = true, bool inherits = false, string defaultValue = null) { return GetDataAttribute("server-" + attributeName, htmlDecode, inherits, defaultValue); }
		public string GetAttribute(string attributeName, bool htmlDecode = true, bool inherits = false, string defaultValue = null)
		{
			string value;
			if (m_Attributes != null && m_Attributes.TryGetValue(CheckAttributeName(attributeName), out value))
			{
				if (!htmlDecode)
					return value;
				else
					return HttpUtility.HtmlDecode(value);
			}
			if (inherits && Parent != null && Parent is HtmlElement)
				return ((HtmlElement)Parent).GetAttribute(attributeName, htmlDecode, inherits, defaultValue);
			if(!htmlDecode)
				return defaultValue;
			return HttpUtility.HtmlDecode(defaultValue);
		}
		public void SetDataAttribute(string attributeName, string value, bool htmlEncode = true) { SetAttribute("data-" + attributeName, value, htmlEncode); }
		public void SetServerAttribute(string attributeName, string value, bool htmlEncode = true) { SetDataAttribute("server-" + attributeName, value, htmlEncode); }
		public void SetAttribute(string attributeName, uint? value) { SetAttribute(attributeName, value.HasValue ? value.ToString() : null); }
		public void SetAttribute(string attributeName, bool value) { SetAttribute(attributeName, value ? attributeName : null); }
		public void SetAttribute(string attributeName, string value, bool htmlEncode = true)
		{
			if (value == null && m_Attributes == null)
				return;
			if (value == null)
				m_Attributes.Remove(CheckAttributeName(attributeName));
			else
			{
				if(m_Attributes == null)
					m_Attributes = new Dictionary<string, string>();
				if(htmlEncode)
					m_Attributes[CheckAttributeName(attributeName)] = HttpUtility.HtmlEncode(value);
				else
					m_Attributes[CheckAttributeName(attributeName)] = value;
			}
		}

		public string this[string attributeName]
		{
			get { return GetAttribute(attributeName, true); }
			set { SetAttribute(attributeName, value, true); }
		}

		public List<HtmlControl> ChildControls
		{
			get
			{
				if (!AllowChildControls)
					throw new InvalidOperationException(string.Format("<{0}> must be kept empty.", TagName));
				if (m_ChildControls == null)
					m_ChildControls = new List<HtmlControl>();
				return m_ChildControls;
			}
		}
		public HtmlControl AddControl(HtmlControl control)
		{
			ChildControls.Add(control);
			control.Parent = this;
			return control;
		}
		public T AddControl<T>() where T : HtmlControl, new() { return AddControl(new T()) as T; }
		public HtmlText AddText(string text, bool htmlEncode = true) 
		{
			if (text == null)
				return null;
			return AddControl(new HtmlText(text, htmlEncode)) as HtmlText; 
		}
		public HtmlControl[] AddHtml(string html, HtmlControlLoader loader = null)
		{
			HtmlControl[] controls = HtmlControl.LoadHtmlControls(html, loader, Description);
			foreach (HtmlControl control in controls)
				AddControl(control);
			return controls;
		}
		public HtmlComment AddComment(string comment)
		{
			if (comment == null)
				return null;
			return AddControl(new HtmlComment(comment, false)) as HtmlComment;
		}
		public A AddA(string text, string href, bool htmlEncode = true)
		{
			if (href == null)
				return null;
			return AddControl(new A(text, href, htmlEncode)) as A;
		}
		public Img AddImg() { return AddImg(string.Empty, "about:blank"); }
		public Img AddImg(string alt, string src)
		{
			if (alt == null || src == null)
				return null;
			return AddControl(new Img(alt, src)) as Img;
		}
		public T AddText<T>(string text, bool htmlEncode = true) where T:HtmlElement,new()
		{
			T control = new T();
			control.AddText(text, htmlEncode);
			AddControl(control);
			return control;
		}

		public bool RemoveControl(HtmlControl control)
		{
			if (ChildControls.Remove(control))
			{
				control.Parent = null;
				return true;
			}
			return false;
		}

		public override void ProcessBinRequest(HttpHandlerContext context)
		{
			if (m_ChildControls != null)
				foreach (HtmlControl control in m_ChildControls)
					control.ProcessBinRequest(context);
		}

		public override void ProcessHtmlRequest(HttpHandlerContext context)
		{
			if (m_ChildControls != null)
				foreach (HtmlControl control in m_ChildControls)
					control.ProcessHtmlRequest(context);
		}
		public override void ProcessJsRequest(HttpHandlerContext context)
		{
			if (m_ChildControls != null)
				foreach (HtmlControl control in m_ChildControls)
					control.ProcessJsRequest(context);
		}
		public override void ProcessCssRequest(HttpHandlerContext context)
		{
			if (m_ChildControls != null)
				foreach (HtmlControl control in m_ChildControls)
					control.ProcessCssRequest(context);
		}

		public override void Render(System.Web.UI.HtmlTextWriter writer, RenderMode mode, int level, bool innerHtml = false)
		{
			if (!ServerRender)
				return;
			if (innerHtml && (m_ChildControls == null || m_ChildControls.Count == 0))
				return;
			if (mode == RenderMode.Debug || mode == RenderMode.XHtml)
			{
				for (int c = 0; c < level; c++)
					writer.Write("\t");
				if (mode == RenderMode.Debug)
					writer.Write("-->");
			}
			if (!innerHtml)
			{
				writer.Write('<');
				writer.Write(TagName);
				if (m_Attributes != null)
					foreach (KeyValuePair<string, string> pair in m_Attributes)
						writer.Write(string.Format(" {0}=\"{1}\"", pair.Key, pair.Value));
			}
			if (Description.ForceClosingTag || (m_ChildControls != null && m_ChildControls.Count > 0))
			{
				if(!innerHtml)
					writer.Write('>');
				bool indent = false;
				if ((mode == RenderMode.Debug || mode == RenderMode.XHtml) && m_ChildControls != null && m_ChildControls.Count > 0)
				{
					if (m_ChildControls.Count > 1 || !(m_ChildControls[0] is HtmlText))
					{
						if (mode == RenderMode.Debug)
							writer.Write("<!--");
						writer.WriteLine();
						indent = true;
					}
				}
				if (m_ChildControls != null)
					foreach (HtmlControl control in m_ChildControls)
						control.Render(writer, mode, indent ? level + 1 : 0);

				if ((mode == RenderMode.Debug || mode == RenderMode.XHtml) && indent)
				{
					for (int c = 0; c < level; c++)
						writer.Write("\t");
					if (mode == RenderMode.Debug)
						writer.Write("-->");
				}
				if (!innerHtml)
				{
					writer.Write("</");
					writer.Write(TagName);
				}
			}
			else if(!innerHtml)
				writer.Write(" /");
			if (!innerHtml)
			{
				writer.Write('>');
				if (mode == RenderMode.Debug)
					writer.Write("<!--");
				if (mode == RenderMode.XHtml || mode == RenderMode.Debug)
					writer.WriteLine();
			}
		}

		public delegate bool FilterDelegate(HtmlElement control);
		public delegate bool RecursionDelegate(HtmlElement control);
		public IEnumerable<T> GetElements<T>(FilterDelegate filter, RecursionDelegate recurse) where T:HtmlElement
		{
			if (this is T && (filter == null || filter(this)))
				yield return this as T;
			if(m_ChildControls != null && m_ChildControls.Count > 0 && (recurse == null || recurse(this)))
				foreach (HtmlControl control in m_ChildControls)
				{
					if (control is HtmlElement)
					{
						foreach (T t in (control as HtmlElement).GetElements<T>(filter, recurse))
							yield return t;
					}
				}

		}
		public T GetElement<T>(FilterDelegate filter, RecursionDelegate recurse) where T : HtmlElement
		{
			foreach (T t in GetElements<T>(filter, recurse))
				return t;
			return null;
		}

		public T GetElementById<T>(string id) where T: HtmlElement { return GetElementByAttribute<T>("id", id); }
		public T GetElementByServerId<T>(string id) where T : HtmlElement { return GetElementByServerId<T>(id, false); }
		public T GetElementByServerId<T>(string id, bool assignId) where T : HtmlElement
		{
			T t = GetElementByAttribute<T>("data-server-id", id);
			if (t != null)
			{
				if (assignId && t.Id == null)
					t.Id = id;
			}
			return t;
		}
		public T GetElementByServerId<T>(string id, bool assignId, bool assignName) where T : HtmlElement, WithName { return GetElementByServerId<T>(id, assignId, assignName ? id : null); }
		public T GetElementByServerId<T>(string id, bool assignId, string defaultName) where T : HtmlElement, WithName
		{
			T t = GetElementByServerId<T>(id, assignId);
			if (t != null)
			{
				if (!string.IsNullOrWhiteSpace(defaultName) && t.Name == null)
					t.Name = defaultName;
			}
			return t;
		}

		public T GetElementByAttribute<T>(string attributeName, string attributeValue) where T : HtmlElement
		{
			if (attributeValue.Equals(this[attributeName]))
				return this as T;
			if (!AllowChildControls || m_ChildControls == null || m_ChildControls.Count == 0)
				return null;
			foreach (HtmlControl control in m_ChildControls)
				if (control is HtmlElement)
				{
					HtmlElement elt = control as HtmlElement;
					elt = elt.GetElementByAttribute<T>(attributeName, attributeValue);
					if (elt != null)
						return elt as T;
				}
			return null;
		}

		public T GetElementByTagName<T>(string tagName) where T : HtmlElement
		{
			foreach (T t in GetElementsByTagName<T>(tagName))
				return t;
			return null;
		}
		public IEnumerable<T> GetElementsByTagName<T>(string tagName) where T : HtmlElement
		{
			if (tagName.Equals(TagName) && this is T)
				yield return this as T;
			if (!AllowChildControls || m_ChildControls == null || m_ChildControls.Count == 0)
				yield break;
			foreach (HtmlControl control in m_ChildControls)
				if (control is HtmlElement)
				{
					HtmlElement elt = control as HtmlElement;
					foreach(T t in elt.GetElementsByTagName<T>(tagName))
						yield return t;
				}
		}
		public IEnumerable<T> GetElementsByName<T>(string name) where T : HtmlElement, WithName
		{
			if (this is T && name.Equals(((T)this).Name))
				yield return this as T;
			if (!AllowChildControls || m_ChildControls == null || m_ChildControls.Count == 0)
				yield break;
			foreach (HtmlControl control in m_ChildControls)
				if (control is HtmlElement)
				{
					HtmlElement elt = control as HtmlElement;
					foreach (T t in elt.GetElementsByName<T>(name))
						yield return t;
				}
		}
		public IEnumerable<Input> GetInputByType(HtmlInputType type)
		{
			if (this is Input && ((Input)this).Type == type)
				yield return this as Input;
			if (!AllowChildControls || m_ChildControls == null || m_ChildControls.Count == 0)
				yield break;
			foreach (HtmlControl control in m_ChildControls)
				if (control is HtmlElement)
				{
					HtmlElement elt = control as HtmlElement;
					foreach (Input t in elt.GetInputByType(type))
						yield return t;
				}
		}
		public IEnumerable<T> GetElementsByClass<T>(string className) where T : HtmlElement
		{
			string classValue = this["class"];
			if (this is T && !string.IsNullOrWhiteSpace(classValue))
			{
				string[] classes = classValue.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
				HashSet<string> setClasses = new HashSet<string>(classes);
				if (setClasses.Contains(className))
					yield return this as T;
			}
			if (!AllowChildControls || m_ChildControls == null || m_ChildControls.Count == 0)
				yield break;
			foreach (HtmlControl control in m_ChildControls)
				if (control is HtmlElement)
				{
					HtmlElement elt = control as HtmlElement;
					foreach (T t in elt.GetElementsByClass<T>(className))
						yield return t;
				}
		}

		public IEnumerable<T> GetControls<T>() where T : HtmlElement
		{
			if (!AllowChildControls || m_ChildControls == null || m_ChildControls.Count == 0)
				yield break;
			foreach (HtmlControl control in m_ChildControls)
				if (control is T)
					yield return control as T;
		}

		public string InnerText
		{
			get
			{
				StringBuilder builder = new StringBuilder();
				if(m_ChildControls != null)
					foreach (HtmlControl control in m_ChildControls)
					{
						if (control is HtmlElement)
							builder.Append((control as HtmlElement).InnerText);
						else if (control is HtmlText)
							builder.Append((control as HtmlText).Text);
					}
				return builder.ToString();
			}
			set
			{
				if(Description != null && !Description.AllowInnerText)
					throw new InvalidOperationException(string.Format("<{0}> does not allow inner text.", TagName));
				if(m_ChildControls == null)
					m_ChildControls= new List<HtmlControl>();
				if (m_ChildControls.Count == 0)
					m_ChildControls.Add(new HtmlText());
				if (m_ChildControls == null || m_ChildControls.Count != 1 || !(m_ChildControls[0] is HtmlText))
					throw new InvalidOperationException("Can only set inner text when there is exactly one text control.");
				(m_ChildControls[0] as HtmlText).Text = value;
			}
		}

		public override void Load(System.Xml.XmlNode node, HtmlControlLoader loader)
		{
			if (!TagName.Equals(node.Name) && !loader.Handle(TagName, node))
				throw new ArgumentException(string.Format("Invalid node name, should be '{0}'.", TagName), "node.Name");
			foreach (XmlAttribute attribute in node.Attributes)
				this[attribute.Name] = HttpUtility.HtmlDecode(attribute.Value);
			string serverRender = GetServerAttribute("render");
			string serverSafeId = GetServerAttribute("safe-id");
			if (serverRender == "false")
				ServerRender = false;
			else if (serverRender == "true")
				ServerRender = true;
			if (serverSafeId == "false")
				ServerSafeId = false;
			else if (serverSafeId == "true")
				ServerSafeId = true;
			foreach (XmlNode child in node.ChildNodes)
			{
				HtmlControl control = HtmlControl.LoadControl(child, loader);
				if (control != null)
					AddControl(control);
			}
		}

		public override int CleanAttributes(Regex regex, bool recursive)
		{
			int count = 0;
			if (m_Attributes != null)
			{
				List<string> keys = new List<string>(m_Attributes.Keys);
				foreach (string key in keys)
					if (regex.IsMatch(key))
					{
						this[key] = null;
						count++;
					}
			}
			if (recursive && m_ChildControls != null)
			{
				foreach(HtmlControl control in m_ChildControls)
					if (control is HtmlElement)
						count += (control as HtmlElement).CleanAttributes(regex, true);
			}
			return count;
		}

		public override void ProcessJsonRequest(HttpHandlerContext context)
		{
			if(m_ChildControls != null)
				foreach (HtmlControl control in m_ChildControls)
					control.ProcessJsonRequest(context);
		}

		#region Global attributes
		public string Id { get { return this["id"]; } set { this["id"] = value; } }
		public string Lang { get { return GetAttribute("lang", false, true); } set { this["lang"] = value; } }
		public string Title { get { return this["title"]; } set { this["title"] = value; } }
		public uint? TabIndex { get { return GetAttributeUint("tabindex"); } set { SetAttribute("tabindex", value); } }
		public bool Translate { get { return GetAttribute("translate", false, true) != "no"; } set { SetAttribute("translate", value ? "yes" : "no"); } }
		public string AccessKey { get { return this["accesskey"]; } set { this["accesskey"] = value; } }
		public bool ContentEditable { get { return GetAttribute("contenteditable", false, true, "false") == "true"; } set { SetAttribute("contenteditable", value ? "true" : "false"); } }
		public string ContextMenu { get { return this["contextmenu"]; } set { this["contextmenu"] = value; } }
		public HtmlDirection Dir { get { return (HtmlDirection)Enum.Parse(typeof(HtmlDirection), GetAttribute("dir", false, false, "auto"), true); } set { this["dir"] = value.ToString().ToLowerInvariant(); } }
		public bool? Draggable
		{
			get
			{
				switch (this["draggable"])
				{
					case "true": return true;
					case "false": return false;
				}
				return null;
			}
			set
			{
				if (value.HasValue)
				{
					if (value.Value)
						this["draggable"] = "true";
					else
						this["draggable"] = "false";
				}
				else
					this["draggable"] = null;
			}
		}
		public string DropZone { get { return this["dropzone"]; } set { this["dropzone"] = value; } }
		public bool Hidden { get { return GetAttributeBool("hidden"); } set { SetAttribute("hidden", value); } }
		
		public bool SpellCheck { get { return this["spellcheck"] == "true" || this["spellcheck"] == string.Empty; } set { this["spellcheck"] = value ? "true" : "false"; } }

		private TokenSet m_Classes;
		public string Class { get { return this["class"]; } set { m_Classes = null; this["class"] = value; } }
		public TokenSet Classes
		{
			get
			{
				if (m_Classes == null)
				{
					m_Classes = new TokenSet(Class);
					m_Classes.Change += new TokenSet.TokenSetChanged(OnClassesChange);
				}
				return m_Classes;
			}
		}
		private void OnClassesChange(TokenSet set)
		{
			if (set == m_Classes)
			{
				string classes = set.ToString();
				if(string.IsNullOrWhiteSpace(classes))
					classes = null;
				this["class"] = classes;
			}
		}

		public string Style { get { return this["style"]; } set { m_CssProperties = null; this["style"] = value; } }
		private CssProperties m_CssProperties;
		public CssProperties CssProperties
		{
			get
			{
				if (m_CssProperties == null)
				{
					m_CssProperties = new CssProperties(Style);
					m_CssProperties.Change += OnCssPropertiesChange;
				}
				return m_CssProperties;
			}
		}
		private void OnCssPropertiesChange(object source, EventArgs args)
		{
			if (source == m_CssProperties)
			{
#if DEBUG
				m_CssProperties.CheckValidity();
#endif
				this["style"] = m_CssProperties.ToString();
			}
		}
		#endregion
		#region Js client events
		public string OnAbort { get { return this["onabort"]; } set { this["onabort"] = value; } }
		public string OnBlur { get { return this["onblur"]; } set { this["onblur"] = value; } }
		public string OnCanPlay { get { return this["oncanplay"]; } set { this["oncanplay"] = value; } }
		public string OnCanPlayThrough { get { return this["oncanplaythrough"]; } set { this["oncanplaythrough"] = value; } }
		public string OnChange { get { return this["onchange"]; } set { this["onchange"] = value; } }
		public string OnClick { get { return this["onclick"]; } set { this["onclick"] = value; } }
		public string OnContextMenu { get { return this["oncontextmenu"]; } set { this["oncontextmenu"] = value; } }
		public string OnDblClick { get { return this["ondblclick"]; } set { this["ondblclick"] = value; } }
		public string OnDrag { get { return this["ondrag"]; } set { this["ondrag"] = value; } }
		public string OnDragEnd { get { return this["ondragend"]; } set { this["ondragend"] = value; } }
		public string OnDragEnter { get { return this["ondragenter"]; } set { this["ondragenter"] = value; } }
		public string OnDragLeave { get { return this["ondragleave"]; } set { this["ondragleave"] = value; } }
		public string OnDragOver { get { return this["ondragover"]; } set { this["ondragover"] = value; } }
		public string OnDragStart { get { return this["ondragstart"]; } set { this["ondragstart"] = value; } }
		public string OnDrop { get { return this["ondrop"]; } set { this["ondrop"] = value; } }
		public string OnDurationChange { get { return this["ondurationchange"]; } set { this["ondurationchange"] = value; } }
		public string OnEmptied { get { return this["onemptied"]; } set { this["onemptied"] = value; } }
		public string OnEnded { get { return this["onended"]; } set { this["onended"] = value; } }
		public string OnError { get { return this["onerror"]; } set { this["onerror"] = value; } }
		public string OnFocus { get { return this["onfocus"]; } set { this["onfocus"] = value; } }
		public string OnInput { get { return this["oninput"]; } set { this["oninput"] = value; } }
		public string OnInvalid { get { return this["oninvalid"]; } set { this["oninvalid"] = value; } }
		public string OnKeyDown { get { return this["onkeydown"]; } set { this["onkeydown"] = value; } }
		public string OnKeyPress { get { return this["onkeypress"]; } set { this["onkeypress"] = value; } }
		public string OnKeyUp { get { return this["onkeyup"]; } set { this["onkeyup"] = value; } }
		public string OnLoad { get { return this["onload"]; } set { this["onload"] = value; } }
		public string OnLoadedData { get { return this["onloadeddata"]; } set { this["onloadeddata"] = value; } }
		public string OnLoadedMetaData { get { return this["onloadedmetadata"]; } set { this["onloadedmetadata"] = value; } }
		public string OnLoadStart { get { return this["onloadstart"]; } set { this["onloadstart"] = value; } }
		public string OnMouseDown { get { return this["onmousedown"]; } set { this["onmousedown"] = value; } }
		public string OnMouseMove { get { return this["onmousemove"]; } set { this["onmousemove"] = value; } }
		public string OnMouseOut { get { return this["onmouseout"]; } set { this["onmouseout"] = value; } }
		public string OnMouseOver { get { return this["onmouseover"]; } set { this["onmouseover"] = value; } }
		public string OnMouseUp { get { return this["onmouseup"]; } set { this["onmouseup"] = value; } }
		public string OnMouseWheel { get { return this["onmousewheel"]; } set { this["onmousewheel"] = value; } }
		public string OnPause { get { return this["onpause"]; } set { this["onpause"] = value; } }
		public string OnPlay { get { return this["onplay"]; } set { this["onplay"] = value; } }
		public string OnPlaying { get { return this["onplaying"]; } set { this["onplaying"] = value; } }
		public string OnProgress { get { return this["onprogress"]; } set { this["onprogress"] = value; } }
		public string OnRateChange { get { return this["onratechange"]; } set { this["onratechange"] = value; } }
		public string OnReadyStateChange { get { return this["onreadystatechange"]; } set { this["onreadystatechange"] = value; } }
		public string OnReset { get { return this["onreset"]; } set { this["onreset"] = value; } }
		public string OnScroll { get { return this["onscroll"]; } set { this["onscroll"] = value; } }
		public string OnSeeked { get { return this["onseeked"]; } set { this["onseeked"] = value; } }
		public string OnSeeking { get { return this["onseeking"]; } set { this["onseeking"] = value; } }
		public string OnSelect { get { return this["onselect"]; } set { this["onselect"] = value; } }
		public string OnShow { get { return this["onshow"]; } set { this["onshow"] = value; } }
		public string OnStalled { get { return this["onstalled"]; } set { this["onstalled"] = value; } }
		public string OnSubmit { get { return this["onsubmit"]; } set { this["onsubmit"] = value; } }
		public string OnSuspend { get { return this["onsuspend"]; } set { this["onsuspend"] = value; } }
		public string OnTimeUpdate { get { return this["ontimeupdate"]; } set { this["ontimeupdate"] = value; } }
		public string OnVolumeChange { get { return this["onvolumechange"]; } set { this["onvolumechange"] = value; } }
		public string OnWaiting { get { return this["onwaiting"]; } set { this["onwaiting"] = value; } }
		#endregion
		#region Server attributes
		public bool ServerSafeId { get { return m_ServerSafeId; } set { m_ServerSafeId = value; } }
		public string ServerId { get { return GetServerAttribute("id", false); } set { SetServerAttribute("id", value, false); } }
		public string ServerType { get { return GetServerAttribute("type", false); } set { SetServerAttribute("type", value, false); } }
		public string ServerLoad { get { return GetServerAttribute("load", false); } set { SetServerAttribute("load", value, false); } }
		public bool ServerTranslation { get { return GetServerAttribute("translation", false, false, "true") == "true"; } set { SetServerAttribute("translation", value ? "true" : "false", false); } }
		public bool ServerAuthenticated { get { return GetServerAttribute("authenticated") == "true"; } set { SetServerAttribute("authenticated", value ? "true" : null); } }
		public bool ServerNotAuthenticated { get { return GetServerAttribute("not-authenticated") == "true"; } set { SetServerAttribute("not-authenticated", value ? "true" : null); } }


		#endregion

		public bool NeedTranslation(ITranslator translator)
		{
			string lang = Lang;
			return Translate && ServerTranslation && (lang == null || lang == translator.SourceLang);
		}


		public override void TranslateControl(ITranslator translator)
		{
			base.TranslateControl(translator);
			bool needTranslation = NeedTranslation(translator);
			if (needTranslation)
				TranslateAttributes(translator);
			if (m_ChildControls != null && m_ChildControls.Count > 0)
				foreach (HtmlControl control in m_ChildControls)
					// if translation is need or if it's a sub element (they call this recursively)
					// and as Lang is inherited is only will change if the attribue is explicitly changed
					if(needTranslation || control is HtmlElement)
						control.TranslateControl(translator);
			if (needTranslation && this["lang"] == translator.SourceLang)
				Lang = translator.Lang;
		}

		protected virtual void TranslateAttributes(ITranslator translator)
		{
			Title = translator.Translate(Title);
		}

		public virtual void MarkText(Regex regex, ITranslator translator)
		{
			if (m_ChildControls == null)
				return;
			if (translator != null && NeedTranslation(translator))
				TranslateControl(translator);
			ServerTranslation = false;
			for (int c = 0; c < m_ChildControls.Count; c++)
			{
				HtmlControl control = m_ChildControls[c];
				if (control is HtmlElement)
					(control as HtmlElement).MarkText(regex, translator);
				else if (control is HtmlText)
				{
					HtmlText text = control as HtmlText;
					string original = text.Text;
					if (original == null)
						continue;
					MatchCollection matches = regex.Matches(original);
					if (matches.Count > 0)
					{
						List<HtmlControl> texts = new List<HtmlControl>();
						int lastIndex = 0;
						foreach (Match match in matches)
						{
							
							int index = match.Index;
							if (lastIndex > index)
								continue;
							else if (index > lastIndex)
							{
								string notMatchText = original.Substring(lastIndex, index - lastIndex);
								texts.Add(new HtmlText(notMatchText));
							}
							string matchText = match.Value;
							lastIndex = match.Index + match.Length;
							texts.Add(new Mark(matchText));
						}
						if(lastIndex < original.Length)
							texts.Add(new HtmlText(original.Substring(lastIndex)));
						m_ChildControls.RemoveAt(c);
						m_ChildControls.InsertRange(c, texts);
						c += (texts.Count - 1);
					}
				}
			}
		}
	}
}
